package com.xaaef.authorize.server.service.impl;

import com.xaaef.authorize.common.domain.ClientDetails;
import com.xaaef.authorize.common.domain.TokenValue;
import com.xaaef.authorize.common.domain.UserInfo;
import com.xaaef.authorize.common.enums.GrantType;
import com.xaaef.authorize.server.params.GetCodeModeParam;
import com.xaaef.authorize.server.params.TencentModeParam;
import com.xaaef.authorize.server.props.OAuth2ServerProperties;
import com.xaaef.authorize.server.service.TokenCacheManager;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;

import static com.xaaef.authorize.common.util.JsonUtils.DEFAULT_DATE_TIME_PATTERN;
import static com.xaaef.authorize.server.constant.TokenConstant.*;
import static com.xaaef.authorize.server.constant.TokenConstant.FORCED_OFFLINE_KEY;

/**
 * All rights Reserved, Designed By 深圳市铭灏天智能照明设备有限公司
 * <p>
 * </p>
 *
 * @author Wang Chen Chen
 * @version 1.0.1
 * @date 2021/8/3 15:48
 * @copyright 2021 http://www.mhtled.com Inc. All rights reserved.
 */

@Slf4j
@Service
@AllArgsConstructor
public class RedisTokenCacheManager implements TokenCacheManager {

    private RedisTemplate<String, Object> redisTemplate;

    private OAuth2ServerProperties props;

    @Override
    public TokenValue getAccessToken(String tokenId) {
        TokenValue token = (TokenValue) redisTemplate.opsForValue().get(LOGIN_TOKEN_KEY + tokenId);
        return token;
    }

    @Override
    public void setAccessToken(String tokenId, TokenValue tokenValue) {
        tokenValue.setTokenId(tokenId);
        // 将随机id 跟 当前登录的用户关联，在一起！
        redisTemplate.opsForValue().set(
                LOGIN_TOKEN_KEY + tokenId,
                tokenValue,
                props.getTokenExpired(),
                TimeUnit.SECONDS
        );
        // 如果是 客户端模式，那么 就没有 单点登录
        if (tokenValue.getGrantType() == GrantType.CLIENT_CREDENTIALS) {
            return;
        }
        // 其他模式，判断是否开启 单点登录
        if (props.getSso()) {
            UserInfo loginUser = tokenValue.getUser();

            // 获取使用 此用户登录的 上个用户的 tokenId
            String oldTokenId = getOnlineUser(loginUser.getUsername());

            // 判断用户名。是否已经登录了！
            if (StringUtils.hasText(oldTokenId)) {
                // 移除 之前登录的 用户 token 以及 refresh_token
                removeAccessToken(oldTokenId);
                removeRefreshToken(oldTokenId);

                // 移除 之前登录的在线用户
                removeOnlineUser(loginUser.getUsername());

                // 获取当前时间
                String milli = LocalDateTime.now().format(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_PATTERN));

                // 将 被强制挤下线的用户，以及时间，保存到 redis中，提示给前端用户！
                setForcedOffline(oldTokenId, milli);

            }

            // 设置 在线用户，为新用户
            setOnlineUser(loginUser.getUsername(), tokenId);
        }
    }

    @Override
    public void removeAccessToken(String tokenId) {
        redisTemplate.delete(LOGIN_TOKEN_KEY + tokenId);
    }

    @Override
    public TokenValue getRefreshToken(String tokenId) {
        TokenValue token = (TokenValue) redisTemplate.opsForValue().get(REFRESH_TOKEN_KEY + tokenId);
        return token;
    }

    @Override
    public void setRefreshToken(String tokenId, TokenValue tokenValue) {
        tokenValue.setTokenId(tokenId);
        // 将随机id 跟 当前登录的用户关联，在一起！
        redisTemplate.opsForValue().set(
                REFRESH_TOKEN_KEY + tokenId,
                tokenValue,
                props.getTokenExpired(),
                TimeUnit.SECONDS
        );
    }

    @Override
    public void removeRefreshToken(String tokenId) {
        redisTemplate.delete(REFRESH_TOKEN_KEY + tokenId);
    }

    @Override
    public GetCodeModeParam getCodeMode(String codeKey) {
        return (GetCodeModeParam) redisTemplate.opsForValue().get(CODE_MODE_KEY + codeKey);
    }

    @Override
    public void setCodeMode(String codeKey, GetCodeModeParam param) {
        redisTemplate.opsForValue().set(CODE_MODE_KEY + codeKey, param, props.getCodeExpired(), TimeUnit.SECONDS);
    }

    @Override
    public void removeCodeMode(String codeKey) {
        redisTemplate.delete(CODE_MODE_KEY + codeKey);
    }

    @Override
    public UserInfo getLoginUser(String code) {
        return (UserInfo) redisTemplate.opsForValue().get(CODE_KEY + code);
    }

    @Override
    public void setLoginUser(String code, UserInfo loginUser) {
        redisTemplate.opsForValue().set(CODE_KEY + code, loginUser, props.getCodeExpired(), TimeUnit.SECONDS);
    }

    @Override
    public void removeLoginUser(String code) {
        redisTemplate.delete(CODE_KEY + code);
    }

    @Override
    public String getString(String key) {
        return (String) redisTemplate.opsForValue().get(key);
    }

    @Override
    public void setString(String key, String value, long timeout, TimeUnit unit) {
        redisTemplate.opsForValue().set(key, value, timeout, unit);
    }

    @Override
    public void removeString(String key) {
        redisTemplate.delete(key);
    }

    @Override
    public String getOnlineUser(String username) {
        return (String) redisTemplate.opsForValue().get(ONLINE_USER_KEY + username);
    }

    @Override
    public void setOnlineUser(String username, String tokenId) {
        redisTemplate.opsForValue().set(ONLINE_USER_KEY + username, tokenId, props.getTokenExpired(), TimeUnit.SECONDS);
    }

    @Override
    public void removeOnlineUser(String username) {
        redisTemplate.delete(ONLINE_USER_KEY + username);
    }


    @Override
    public String getForcedOffline(String tokenId) {
        return (String) redisTemplate.opsForValue().get(FORCED_OFFLINE_KEY + tokenId);
    }

    @Override
    public void setForcedOffline(String tokenId, String offlineTime) {
        redisTemplate.opsForValue().set(FORCED_OFFLINE_KEY + tokenId, offlineTime, props.getPromptExpired(), TimeUnit.SECONDS);
    }

    @Override
    public void removeForcedOffline(String tokenId) {
        redisTemplate.delete(FORCED_OFFLINE_KEY + tokenId);
    }

    @Override
    public TencentModeParam getTencentMode(String state) {
        return (TencentModeParam) redisTemplate.opsForValue().get(TENCENT_MODE_KEY + state);
    }

    @Override
    public void setTencentMode(String state, TencentModeParam param) {
        redisTemplate.opsForValue().set(TENCENT_MODE_KEY + state, param, props.getCodeExpired(), TimeUnit.SECONDS);
    }

    @Override
    public void removeTencentMode(String state) {
        redisTemplate.delete(TENCENT_MODE_KEY + state);
    }

}
